#include "Bloque.h"

#include "../Bytes.h"
#include "Registro.h"
#include "RegistroVariable.h"
#include "../FuncionesUtiles.h"
#include "../claves/TipoClave.h"
#include "../claves/Key.h"

#include <list>
using namespace std;

Bloque::Bloque(cantBytes tamanio) {
	this->tamanio = tamanio;
	this->usados = 0;
}

Bloque::~Bloque() {

}

void Bloque::hidratarse(const Bytes& bytesBloque) {

	cantBytes tamanio = bytesBloque.getTamanio();
	offset offset = 0;

	if (tamanio < 8) {
		cerr << "El tamanio del bloque es muy chico (" << tamanio << ")"<< endl;
		return;
	}

	bool seguir = true;

	while (seguir) {

		cantBytes tamanioDato = bytesToCantBytes(bytesBloque.getSubBytes(
				offset, LONGITUD_CANT_BYTES));

		if (tamanioDato == 0) {
			break;
		}

		offset += sizeof(cantBytes);

		Bytes dato = bytesBloque.getSubBytes(offset,tamanioDato);
		RegistroVariable* registro = new RegistroVariable(dato);
		agregarRegistro(registro);

		offset += tamanioDato;

		// si no entra ningun registro mas en el bloque, osea se llego al final
		if (offset >= tamanio - sizeof(cantBytes)) {
			seguir =  false;
		}
	}

	this->tamanio = tamanio;
	this->usados = offset;

}

bool Bloque::agregarRegistro(RegistroVariable* registro) {

	cantBytes tamanioSerializado = registro->getTamanioSerializado();

	if (!tieneLugar(registro)) {
		cerr << "Error: tratando de agregar un registro que no tiene lugar" << endl;
		return false;
	}

	this->agregarComponente(registro);
	this->usados += tamanioSerializado;

	return true;
}

Bytes Bloque::serializarse() {

	Bytes serializacion;

	list<Componente*>::iterator it = this->componentes.begin();

	for ( ; it != this->componentes.end(); it++) {
		Bytes registroSerializado = (*it)->serializarse();
		serializacion.agregarAlFinal(registroSerializado);

	}


	Bytes bytesNulos = crearBytesNulos(this->tamanio-this->usados);
	serializacion.agregarAlFinal(bytesNulos);

	return serializacion;
}

bool Bloque::tieneLugar(RegistroVariable* registro) {

	cantBytes tamanioDelNuevo = registro->getTamanioSerializado();

	if (((double) PORCENTAJE_BLOQUE_LLENO / 100) * this->tamanio
			>= tamanioDelNuevo + this->usados) {
		return true;
	} else {
		return false;
	}
}

cantBytes Bloque::getTamanioSerializado() {
	return this->tamanio;
}


Bytes Bloque::obtener(Bytes clave, TipoClave* extractor) {

	list<Componente*>::iterator it = this->componentes.begin();

	while (it != this->componentes.end()) {
		RegistroVariable* registro = (RegistroVariable*)*it;
		Bytes dato = registro->getDato();
		Bytes claveDato = extractor->extraerClave(dato);
		if  (clave.toString() == claveDato.toString()) {
			return dato;
		}
		it++;
	}

	return Bytes("");
}


bool Bloque::eliminar(Bytes clave, TipoClave* extractor) {

	list<Componente*>::iterator it = this->componentes.begin();

	while (it != this->componentes.end()) {
		RegistroVariable* registro = (RegistroVariable*)*it;
		Bytes dato = registro->getDato();
		Bytes claveDato = extractor->extraerClave(dato);
		if  (clave.toString() == claveDato.toString()) {
			this->componentes.erase(it);
			this->usados -= registro->getTamanioSerializado();
			delete registro;
			return true;
		}
		it++;
	}

	return false;
}

